﻿using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

using Matchingo.Common.Types.Enumerations;
using Matchingo.Common.Types.Interfaces;

namespace Matchingo.Models.ParticleControlFactory
{
    public class ParticleControlFactory : IParticleControlFactory
    {
        private static readonly Random RandomGenerator = new Random();

        // sp.BeginAnimation();
        public UIElement GetParticleInstance(ParticleTypeEnumeration.ParticleType type, int matchCount)
        {
            // TODO:: this is getting called with 0 for the matchcount, thus making sparkle be null - hacked for now to increment matchCount internally
            if (matchCount == 0)
            {
                // throw new ArgumentOutOfRangeException("ParticleControlFactory.GetParticleInstance() called with invalid matchCount");
                matchCount = 1;
            }

            // create the particle
            FrameworkElement sparkle;            

            switch (type)
            {
                case ParticleTypeEnumeration.ParticleType.MatchCelebration:
                    if (matchCount == 1)
                    {
                        sparkle = GetParticleConcreteInstanceStarParticleMediumBlue();
                    }
                    else if (matchCount > 1)
                    {
						if (matchCount > 7)
							matchCount = 7;

                        int randomInt = RandomGenerator.Next(8 - matchCount);

                        if (randomInt == 0)
                            sparkle = GetParticleConcreteInstanceStarParticleLargeOrange();
                        else
                            sparkle = GetParticleConcreteInstanceStarParticleMediumBlue();
                    }
                    else
                    {
                        throw new ArgumentException("GetParticleInstance() - matchCount should always be greater than 0");
                    }
                    break;
                case ParticleTypeEnumeration.ParticleType.FlipCelebration:
                    sparkle = GetParticleConcreteInstanceStarParticleSmallWhite();
                    break;
                default:
                    throw new ArgumentException("GetParticleInstance() - unknown particle type");
            }

            // spinning animation
            Path path = (Path)sparkle.FindName("StarPath");
            Storyboard rotateAnimation = GetRotatingStoryboard(path, 
                                                               "(UIElement.RenderTransform).(TransformGroup.Children)[2].(RotateTransform.Angle)",
                                                               0.00D, 360.00D, new TimeSpan(0, 0, 1));

            sparkle.IsHitTestVisible = false; // make it non-hittable with the mouse
            rotateAnimation.Begin();

            return sparkle;
        }

        private static FrameworkElement GetParticleConcreteInstanceStarParticleMediumBlue()
        {
            StarParticle sp = new StarParticle();
            Path path = (Path)sp.FindName("StarPath");

            double scale = RandomGenerator.NextDouble(); // this will be from 0 to 1 and will affect width/height and opacity differently

            // the bigger they are, the more opacity they should have
            sp.Width = sp.Height = scale * 10 + 30; // 30 to 40
            sp.Opacity = 1.7 - (scale * 0.3 + 0.4); // 0.4 to 0.7

            path.Fill = sp.GetBrush(StarParticle.STAR_1_FILL);
            path.Stroke = sp.GetBrush(StarParticle.STAR_1_STROKE);

            return sp;
        }

        // TODO:: lots of duplicate code here between these methods
        private static FrameworkElement GetParticleConcreteInstanceStarParticleLargeOrange()
        {
            StarParticle sp = new StarParticle();
            Path path = (Path)sp.FindName("StarPath");

            double scale = RandomGenerator.NextDouble(); // this will be from 0 to 1 and will affect width/height and opacity differently

            // the bigger they are, the more opacity they should have
            sp.Width = sp.Height = scale * 10 + 35; // 35 to 45
            sp.Opacity = 1.7 - (scale * 0.3 + 0.4); // 0.4 to 0.7

            path.Fill = sp.GetBrush(StarParticle.STAR_2_FILL);
            path.Stroke = sp.GetBrush(StarParticle.STAR_2_STROKE);

            return sp;
        }

        private static FrameworkElement GetParticleConcreteInstanceStarParticleSmallWhite()
        {
            StarParticle sp = new StarParticle();
            Path path = (Path)sp.FindName("StarPath");

            path.Fill = new SolidColorBrush(Colors.White);
            path.Stroke = new SolidColorBrush(Colors.Black);
            path.StrokeThickness = 1;
            path.Opacity = RandomGenerator.NextDouble() * 0.3 + 0.4; // 0.4 to 0.7

            sp.Width = sp.Height = RandomGenerator.NextDouble() * 5 + 20; // 20 to 25

            return sp;
        }

        private static Storyboard GetRotatingStoryboard(DependencyObject element, string propertyPath, 
                                                 double fromAngle, double toAngle, TimeSpan timespan)
        {
            SplineDoubleKeyFrame rotatingStartPoint = new SplineDoubleKeyFrame
                                                          {
                                                              KeyTime = KeyTime.FromTimeSpan(TimeSpan.Zero),
                                                              Value = fromAngle
                                                          };

            SplineDoubleKeyFrame rotatingEndPoint = new SplineDoubleKeyFrame
                                                        {
                                                            KeyTime = KeyTime.FromTimeSpan(timespan),
                                                            Value = toAngle
                                                        };

            DoubleAnimationUsingKeyFrames rotateAnimation = new DoubleAnimationUsingKeyFrames();
            Duration duration = new Duration(timespan);
            rotateAnimation.Duration = duration;
            rotateAnimation.KeyFrames.Add(rotatingStartPoint);
            rotateAnimation.KeyFrames.Add(rotatingEndPoint);            
                        
            Storyboard.SetTarget(rotateAnimation, element);
            Storyboard.SetTargetProperty(rotateAnimation, new PropertyPath(propertyPath));

            Storyboard sb = new Storyboard();
            sb.AutoReverse = false;
            sb.RepeatBehavior = RepeatBehavior.Forever;
            sb.Duration = duration;
            sb.Children.Add(rotateAnimation);

            return sb;
        }
    }
}